//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include <buola/io/cipendpoint.h>

#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>

namespace buola { namespace io {

std::unique_ptr<CSocketAcceptor> CIPEndPoint::GetBoundSocket(ESocketType pType) const
{
    struct addrinfo lHints,*lResult;

    memset(&lHints,0,sizeof(lHints));
    lHints.ai_family=PF_UNSPEC;
    lHints.ai_socktype=(pType&ESocketType::PACKET)?SOCK_SEQPACKET:SOCK_STREAM;
    lHints.ai_flags=AI_PASSIVE;

    int lError=getaddrinfo(mHost.empty()?nullptr:mHost.c_str(),to_string(mPort).c_str(),&lHints,&lResult);

    if(lError) throw XInternal(std::string("can't resolve address: ")+gai_strerror(lError));

    int lFD=-1;

    for(struct addrinfo *lR=lResult;lR;lR=lR->ai_next)
    {
        lFD=::socket(lR->ai_family,lR->ai_socktype,lR->ai_protocol);
        if(lFD<0) continue;
        if(bind(lFD,lR->ai_addr,lR->ai_addrlen)<0)
        {
            close(lFD);
            lFD=-1;
            continue;
        }

        //this avoids the TIMED_WAIT TCP state
        struct linger lLing;
        lLing.l_onoff=1;
        lLing.l_linger=0;
        setsockopt(lFD,SOL_SOCKET,SO_LINGER,&lLing,sizeof(lLing));

        break;
    }

    freeaddrinfo(lResult);

    if(lFD<0) throw XInternal("can't bind socket: "+c_error_string(errno));

    return make_unique<CSocketAcceptor>(nFromFD,lFD,pType);
}

io::PSocket CIPEndPoint::GetConnectedSocket(ESocketType pType) const
{
    struct addrinfo lHints,*lResult;

    memset(&lHints,0,sizeof(lHints));
    lHints.ai_family=PF_UNSPEC;
    lHints.ai_socktype=(pType&ESocketType::PACKET)?SOCK_SEQPACKET:SOCK_STREAM;

    int lError=getaddrinfo(mHost.c_str(),to_string(mPort).c_str(),&lHints,&lResult);

    if(lError) throw XInternal(std::string("can't resolve address: ")+gai_strerror(lError));

    int lFD=-1;
    for(struct addrinfo *lR=lResult;lR;lR=lR->ai_next)
    {
        lFD=::socket(lR->ai_family,lR->ai_socktype,lR->ai_protocol);
        if(lFD<0) continue;
        if(connect(lFD,lR->ai_addr,lR->ai_addrlen)<0)
        {
            close(lFD);
            lFD=-1;
            continue;
        }
        break;
    }

    freeaddrinfo(lResult);

    if(lFD<0) throw XInternal("can't connect socket: "+c_error_string(errno));

    return new CSocket(nFromFD,lFD,pType);
}

/*namespace io*/ } /*namespace buola*/ }
